package com.mazaiting.instructions.references;

import cn.hutool.core.util.ObjectUtil;
import com.mazaiting.instructions.base.Index16Instruction;
import com.mazaiting.rtda.MtFrame;
import com.mazaiting.rtda.MtOperandStack;
import com.mazaiting.rtda.heap.MtClass;
import com.mazaiting.rtda.heap.MtConstant;
import com.mazaiting.rtda.heap.MtConstantPool;
import com.mazaiting.rtda.heap.MtObject;

import java.util.Objects;

/**
 * 实例指令
 *
 * @author mazaiting
 * @since 2021/8/17
 */
public class InstanceInstruction {

    /**
     * instanceof指令判断对象是否是某个类的实例（或者对象的类是否实现了某个接口），并把结果推入操作数栈。
     * instanceof指令需要两个操作数。第一个操作数是uint16索引，从方法的字节码中获取，
     * 通过这个索引可以从当前类的运行时常量池中找到一个类符号引用。第二个操作数是对象引用，从操作数栈 中弹出
     * <p>
     * 先弹出对象引用，如果是null，则把0推入操作数栈
     * 如果对象引用不是null，则解析类符号引用，判断对象是否是类的实例，然后把判断结果推入操作数栈
     */
    public static class InstanceOf extends Index16Instruction {
        @Override
        public void execute(MtFrame frame) {
            MtOperandStack stack = frame.getOperandStack();
            MtObject ref = stack.popRef();
            if (Objects.isNull(ref)) {
                stack.pushInt(0);
                return;
            }
            // 获取常量池
            MtConstantPool pool = frame.getMethod().getMtClass().getConstantPool();
            // 获取常量池类引用
            MtConstant.MtClassRef classRef = (MtConstant.MtClassRef) pool.getConstant(index);
            // 加载类
            MtClass mtClass = classRef.resolvedClass();
            stack.pushInt(ref.isInstanceOf(mtClass) ? 1 : 0);
        }

        @Override
        public String toString() {
            return "instanceof " + index;
        }
    }

    /**
     * checkcast指令和instanceof指令很像，区别在于：instanceof指令会改变操作数栈（弹出对象引用，推入判断结果）；
     * checkcast则不改变操作数栈（如果判断失败，直接抛出ClassCastException异常）
     *
     * 先从操作数栈中弹出对象引用，再推回去，这样就不会改变操作数栈的状态。如果引用是null，则指令执行结束。
     * 也就是说，null引用可以转换成任何类型，否则解析类符号引用，判断对象是否是类的实例。如果是的话，指令执行结束，
     * 否则抛出ClassCastException
     */
    public static class CheckCast extends Index16Instruction {
        @Override
        public void execute(MtFrame frame) {
            MtOperandStack stack = frame.getOperandStack();
            MtObject ref = stack.popRef();
            stack.pushRef(ref);
            if (Objects.isNull(ref)) {
                return;
            }
            // 常量池
            MtConstantPool pool = frame.getMethod().getMtClass().getConstantPool();
            MtConstant.MtClassRef classRef = (MtConstant.MtClassRef) pool.getConstant(index);
            MtClass mtClass = classRef.resolvedClass();
            if (!ref.isInstanceOf(mtClass)) {
                throw new RuntimeException("java.lang.ClassCastException");
            }
        }

        @Override
        public String toString() {
            return "checkcast " + index;
        }
    }

}


















